package ru.compft.app.as_db.business.impl;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.acls.domain.DefaultPermissionFactory;
import org.springframework.security.acls.domain.GrantedAuthoritySid;
import org.springframework.security.acls.domain.PrincipalSid;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.model.Sid;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import ru.compft.app.as_db.business.BusinessFacade;
import ru.compft.common.dao.AclDao;
import ru.compft.common.data.AppAcl;
import ru.compft.common.data.AppPermission;
import ru.compft.common.data.AppRole;
import ru.compft.common.data.AppUser;
import ru.compft.common.data.acl.AclEntry;
import ru.compft.common.data.acl.AclObjectIdentity;
import ru.compft.common.data.acl.AclSid;
import ru.compft.common.exception.PermissionDeniedException;
import ru.compft.common.services.AclSecurityService;
import ru.compft.common.services.PermissionsService;
import ru.compft.common.services.RolesService;
import ru.compft.common.services.UserService;

import javax.annotation.Resource;
import java.util.*;

/**
 * User: ASUS
 * Date: 28.10.12
 * Time: 19:30
 * Реализация методов бизнес сервиса
 */
@Service
public class BusinessFacadeImpl implements BusinessFacade {
    private static final Logger logger = LoggerFactory.getLogger(BusinessFacadeImpl.class);

    @Resource(name = "userService")
    private UserService userService;

    @Resource(name = "roleService")
    private RolesService rolesService;

    @Resource(name = "permissionsService")
    private PermissionsService permissionsService;

    @Resource(name = "aclSecurityService")
    private AclSecurityService aclSecurityService;

    @Resource(name = "aclDao")
    private AclDao aclDao;

    @Override
    public List<AppUser> showAllUsers() throws PermissionDeniedException {
        return userService.getAllUsers();
    }

    @Override
    public boolean showAddUserPage() throws PermissionDeniedException {
        return true;
    }

    @Override
    public AppUser showEditUserPage(Long userId) throws PermissionDeniedException {
        return userService.findUserById(userId);
    }

    @Override
    public AppUser showViewUserPage(Long userId) {
        return userService.findUserById(userId);
    }

    @Override
    public AppUser saveUser(AppUser newUser) throws PermissionDeniedException {
        return userService.addUser(newUser);
    }

    @Override
    public List<AppRole> showAllRoles() throws PermissionDeniedException {
        return rolesService.getAllRoles();
    }

    @Override
    public boolean showAddRolePage() throws PermissionDeniedException {
        return true;
    }

    @Override
    public AppRole showEditRolePage(Long roleId) throws PermissionDeniedException {
        return rolesService.findRoleById(roleId);
    }

    @Override
    public AppRole showViewRolePage(Long roleId) {
        return rolesService.findRoleById(roleId);
    }

    @Override
    public AppRole saveRole(AppRole appRole) throws PermissionDeniedException {
        return rolesService.addRole(appRole);
    }

    @Override
    public List<AppPermission> showAllPermissions() throws PermissionDeniedException {
        return permissionsService.getAllAppPermission();
    }

    @Override
    public boolean showAddPermissionPage() throws PermissionDeniedException {
        return true;
    }

    @Override
    public AppPermission showEditPermissionPage(Long permissionId) throws PermissionDeniedException {
        return permissionsService.findPermissionById(permissionId);
    }

    @Override
    public AppPermission showViewPermissionPage(Long permissionId) {
        return permissionsService.findPermissionById(permissionId);
    }

    @Override
    public boolean savePermission(AppPermission permission) throws PermissionDeniedException {
        return permissionsService.createPermission(permission);
    }

    @Override
    public void clearCache() throws PermissionDeniedException {
        userService.clearCache();
        rolesService.clearCache();
    }

    /**
     * Список всех ACL записей
     *
     * @return
     * @throws ru.compft.common.exception.PermissionDeniedException
     *
     */
    @Override
    @Transactional(readOnly = true)
    public List<AppAcl> getAclPermissionsForRole(String clazz) throws PermissionDeniedException {
        System.out.println("*** getAclPermissionsForRole clazz = " + clazz);

        final List<AclEntry> allAclEntries = aclDao.getAllAclEntriesByClassName(clazz);

        final List<AppAcl> resultList = new ArrayList<AppAcl>();

        if (!CollectionUtils.isEmpty(allAclEntries)) {
            for (AclEntry aclEntry : allAclEntries) {
                final AclObjectIdentity objectIdentity = aclEntry.getObjectIdentity();
                final AclSid aclSid = aclEntry.getAclSid();

                if (objectIdentity != null && aclSid != null) {
                    // для роли principal равен 0
                    final String principal = aclSid.getPrincipal();

                    if ("0".equals(principal)) {

                        // Маска может быть составная
                        final Long mask = aclEntry.getMask();
                        // парсим маску, превращая ее в массив строк
                        if (mask != null) {
                            final Long[] maskArray = parseMask(mask);

                            // из каждой простой маски делаем объект
                            for (Long simpleMask : maskArray) {
                                final AppAcl appAcl = new AppAcl();

                                appAcl.setClazz(clazz);
                                appAcl.setObjectId(objectIdentity.getObjectIdIdentity());
                                appAcl.setRecipient(aclSid.getSid());
                                appAcl.setMask(simpleMask);
                                appAcl.setPrincipal(Long.valueOf(principal));

                                resultList.add(appAcl);
                            }
                        }
                    }
                }
            }
        }

        System.out.println("*** getAclPermissionsForRole return = " + resultList);
        return resultList;
    }

    /**
     * Сохраняем все установленные права доступа
     *
     * @param appAclList
     */
    @Override
    public void saveAclPermissions(List<AppAcl> appAclList) throws PermissionDeniedException {
        System.out.println("*** saveAclPermissions ");

        if (!CollectionUtils.isEmpty(appAclList)) {
            final DefaultPermissionFactory defaultPermissionFactory = new DefaultPermissionFactory();

            for (AppAcl appAcl : appAclList) {
                // создаем permissions
                final Permission permission = defaultPermissionFactory.buildFromMask(appAcl.getMask().intValue());
                // строим Sid
                final Sid sid = constructSid(appAcl);
                if (sid == null) {
                    logger.warn(appAcl + " hasn't recipient! Nothing will update.");
                    continue;
                }

                aclSecurityService.updatePermission(appAcl.getClazz(), appAcl.getObjectId(), sid, permission);
            }
        }
    }

    public static void main(String[] args) {
        final DefaultPermissionFactory defaultPermissionFactory = new DefaultPermissionFactory();
        final Permission permission = defaultPermissionFactory.buildFromMask(0);

//        final String s = Integer.toString(17, 2);
//        final int length = s.length();
//        for (int i = 0; i < length; i++) {
//            final String substring = s.substring(i, i + 1) + s.substring(i + 1).replaceAll("1", "0");
//            final int i1 = Integer.parseInt(substring, 2);
//            System.out.println("substring = " + i1);
//        }
        System.out.println("permission = " + permission);
    }

    /**
     * Парсим маску раскладывая ее на набор десятичных чисел
     *
     * @return
     */
    private Long[] parseMask(Long mask) {
        final List<Long> resultList = new ArrayList<Long>();

        final String s = Long.toString(mask, 2);
        final int length = s.length();
        for (int i = 0; i < length; i++) {
            // получаем все подстроки из нашего двоичного числа, заменяя все символы кроме первого на 0
            final String substring = s.substring(i, i + 1) + s.substring(i + 1).replaceAll("1", "0");
            final long longValue = Long.parseLong(substring, 2);
            if (longValue > 0) resultList.add(longValue);
        }

        return resultList.toArray(new Long[resultList.size()]);
    }

    /**
     * Строим Sid
     *
     * @param appAcl
     * @return
     */
    private Sid constructSid(AppAcl appAcl) {
        final String recipient = appAcl.getRecipient();

        Sid sid = null;
        final int principal = appAcl.getPrincipal().intValue();
        switch (principal) {
            case 0: {
                sid = new GrantedAuthoritySid(recipient);
                break;
            }
            case 1: {
                sid = new PrincipalSid(recipient);
            }
        }

        return sid;
    }
}
